#pragma once

#include <iostream>
#include <memory>
#include <unordered_map>
#include "Epoller.hpp"
//#include "Listener.hpp"   //Listener.hpp包含了Connection.hpp，就会导致包含两次Connection.hpp，在Connection.hpp有class Reactor; 两个class Reactor;就会导致出错。
#include "Connection.hpp"
#include "Log.hpp"

using namespace LogModule;

class Reactor
{
    static const int revs_num = 128;

private:
    bool IsConnectionExistsHelper(int sockfd)
    {
        auto iter = _connections.find(sockfd);
        if(iter == _connections.end())
            return false;
        return true;
    }

    bool IsConnectionExists(std::shared_ptr<Connection> conn)
    {
        return IsConnectionExistsHelper(conn->GetSockFd());
    }

    bool IsConnectionExists(int sockfd)
    {
        return IsConnectionExistsHelper(sockfd);
    }

    bool IsConnectionEmpty()
    {
        return _connections.empty();
    }

    int LoopOnce(int timeout)
    {
        return _epoller_ptr->WaitEvents(_revs, revs_num, timeout);
    }

    void Dispatcher(int n)
    {
        for(int i = 0; i < n; i++)
        {
            int sockfd = _revs[i].data.fd;
            uint32_t revents = _revs[i].events;

            //将异常处理转化成IO错误，这样可以用一个异常处理函数来完成
            if(revents & EPOLLERR)
                revents |= (EPOLLIN | EPOLLOUT);
            if(revents & EPOLLHUP)
                revents |= (EPOLLIN | EPOLLOUT);
            
            //读事件就绪
            if(revents & EPOLLIN)
            {
                //不需要区分是listensockfd还是普通sockfd
                //通过继承达到指向哪个fd调用哪个Recver
                if(IsConnectionExists(sockfd))
                    _connections[sockfd]->Recver();
            }

            //写事件就绪
            if(revents & EPOLLOUT)
            {
                //写事件默认就绪，直接发送
                //if(IsConnectionExists(sockfd))
                    //_connections[sockfd]->Sender();
            }
        }
    }

public:
    Reactor()
        :_epoller_ptr(std::make_unique<Epoller>())
        ,_isrunning(false)
    {}

    //启动Reactor
    void Loop()
    {
        if(IsConnectionEmpty())
        {
            //连接为空，服务器不能正常启动
            return;
        }

        _isrunning = true;
        int timeout = -1;
        while(_isrunning)
        {
            int n = LoopOnce(timeout);
            Dispatcher(n);
        }
        _isrunning = false;

    }

    void AddConnection(std::shared_ptr<Connection>& conn)
    {
        if(IsConnectionExists(conn))
        {
            LOG(LogLevel::WARNING) << "conn is exists: " << conn->GetSockFd();
            return;
        }
        
        uint32_t events = conn->GetEvent();
        int sockfd = conn->GetSockFd();
        //添加epoll关心的文件描述符和事件
        _epoller_ptr->AddEvent(sockfd, events);
        //设置回值指针
        conn->SetOwner(this);
        //管理新添加的连接
        _connections[sockfd] = conn;
    }

    void EnableReadWrite(int sockfd, bool enbaleRead, bool enableWrite)
    {
        //先判断sockfd受否存在
        if(!IsConnectionExists(sockfd))
        {
            LOG(LogLevel::WARNING) << "EnableReadWrite, conn is not exists: " << sockfd;
            return;
        }

        uint32_t new_events = (EPOLLET | (enbaleRead ? EPOLLIN : 0) | (enableWrite ? EPOLLOUT : 0));
        _connections[sockfd]->SetEvent(new_events);

        _epoller_ptr->ModEvent(sockfd, new_events);
    }

    void DelConnection(int sockfd)
    {
        //1.移除epoll的sockfd时，fd必须是合法的
        _epoller_ptr->DelEvent(sockfd);

        //2. 从connections中移除
        _connections.erase(sockfd);

        close(sockfd);

        LOG(LogLevel::INFO) << "client quit: " << sockfd;
    }

    void PrintConnection()
    {
        std::cout << "当前Reactor正在进行管理的fd List:";
        for(auto &conn : _connections)
        {
            std::cout << conn.second->GetSockFd() << " ";
        }
        std::cout << "\r\n";
    }

    void Stop()
    {
        _isrunning = false;
    }

    ~Reactor()
    {

    }

private:
    std::unique_ptr<Epoller> _epoller_ptr;
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;

    bool _isrunning;
    struct epoll_event _revs[revs_num];
};